/*
 * CITS2230 Operating Systems - Programming Project
 * Author:           Guilherme R. Lampert
 * Student number:   21203005
 */

#ifndef __MEMORYMANAGER_HPP__
#define __MEMORYMANAGER_HPP__

#include <string>

class Process;

// Cache and main memory simulation
class MemoryManager {

public:

	// Constants:

	// The cost of accessing a line from the "cache memory" is 1 time unit
	static const int CacheAccessTime;

	// The cost of accessing a line from the "main memory" is 2 time units
	static const int MainMemoryAccessTime;

	enum MemoryAccessResult { Success, NoMoreCode, PageFault, CacheMiss };

	// Local types:

	struct Page {
		// Each page holds 2 lines of "code"
		// pair.first is the line number based on the file,
		// pair.second is the actual contents of the line.
		std::pair<int, std::string> lines[2];

		// Process owning the "code" in this page.
		const Process * owner;

		// Last time this page was referenced.
		// Important to know so we can evict old pages.
		int lastReferenceTime;

		inline Page & operator = (const Page & rhs)
		{
			this->owner = rhs.owner;
			this->lastReferenceTime = rhs.lastReferenceTime;

			for (int i = 0; i < ARRAY_SIZE(this->lines); ++i)
			{
				this->lines[i] = rhs.lines[i];
			}
			return (*this);
		}

		inline Page(const Page & rhs) : owner(rhs.owner), lastReferenceTime(rhs.lastReferenceTime)
		{
			for (int i = 0; i < ARRAY_SIZE(this->lines); ++i)
			{
				this->lines[i] = rhs.lines[i];
			}
		}

		inline Page() : owner(0), lastReferenceTime(0)
		{
			for (int i = 0; i < ARRAY_SIZE(this->lines); ++i)
			{
				this->lines[i].first = -1;
				this->lines[i].second = "";
			}
		}

		inline bool FindCode(const Process * p, int line, std::string & code) const
		{
			if (p != owner)
			{
				return (false);
			}
			for (int i = 0; i < ARRAY_SIZE(this->lines); ++i)
			{
				if (lines[i].first == line)
				{
					code = lines[i].second;
					return (true);
				}
			}
			return (false);
		}
	};

	// Each frame is equivalent to 1 page
	typedef Page Frame;

	// Methods:

	void PrintMemoryDump(std::ostream & ostr, int timeStamp) const;
	void LoadProcess(Process * p);
	void UnloadProcess(Process * p);
	void ResumeProcess(Process * p) { assert(p != 0); currentProcess = p; }
	MemoryAccessResult NextCodeLine(int & simulationClock, std::string & codeLine);

	MemoryManager();
	~MemoryManager();

private:

	// Disable copy and assignment
	MemoryManager(const MemoryManager &);
	MemoryManager & operator = (const MemoryManager &);

	// Cache management
	MemoryAccessResult ReadFromCache(int line, std::string & code) const;
	MemoryAccessResult LoadToCache(int line, int simulationClock);
	void LoadToMainMemory(int line);

	Process * currentProcess;

	// The size of the cache memory in our simulation will be two pages
	Page cacheMemory[2];

	// The main memory has eight frames and each frame can hold one page
	// that consists of two lines of "code".
	// In other words, the "main memory" can hold 16 lines of "code".
	Frame mainMemory[8];
};

#endif // __MEMORYMANAGER_HPP__
